package the.broccoli.setting.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.wisteria.framework.util.TreeNode;
import org.wisteria.framework.util.TreeSelected;
import org.wisteria.framework.util.TreeUtils;
import org.wisteria.framework.web.BusinessException;
import org.wisteria.framework.web.mvc.constant.WebConsts;
import org.wisteria.framework.web.mvc.dto.ConditionDTO;
import org.wisteria.framework.web.mvc.service.AbstractSuperService;
import org.wisteria.framework.web.mvc.dto.TreeDTO;
import the.broccoli.setting.entity.Dict;
import the.broccoli.setting.mapper.DictMapper;
import the.broccoli.setting.service.IDictService;
import the.broccoli.setting.dto.DictCondition;

import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 字典项 服务实现类
 * </p>
 *
 * @author lei.liu
 * @since 2019-11-27
 */
@Service
//@CacheConfig(cacheNames = "dicts")
public class DictServiceImpl extends AbstractSuperService<DictMapper, Dict> implements IDictService {

    @Autowired
    private CacheManager cacheManager;

    private Cache cache() {
        return cacheManager.getCache("dicts");
    }

    @Transactional
    @Override
    public boolean save(Dict entity) {
        if (!WebConsts.ROOT.equals(entity.getParentId())) {
            Dict parentDict = baseMapper.selectById(entity.getParentId());
            if (parentDict != null) {
                entity.setConstant(parentDict.getConstant());
            }
        }
//        checkConstant(entity.getConstant());
        //checkDictCode(entity.getDictCode());
        super.save(entity);
        cache().put(entity.getDictCode(), entity.getDictName());
        return true;
    }

    @Transactional
    @Override
    public boolean updateById(Dict entity) {
        String dictCode = entity.getDictCode();
        Dict dict = baseMapper.selectById(entity.getDictId());
        if (StringUtils.hasText(dictCode) && !dictCode.equals(dict.getDictCode()) && this.baseMapper.selectByCode(dictCode) != null) {
            throw new BusinessException("编码已经存在相同的");
        }

        super.updateById(entity);
        cache().put(entity.getDictCode(), entity.getDictName());
        return true;
    }

    private void checkDictCode(String dictCode) {
        if (StringUtils.hasText(dictCode) && this.baseMapper.selectByCode(dictCode) != null) {
            throw new BusinessException("编码已经存在相同的");
        }
    }

    private void checkConstant(String constant) {
        if (!StringUtils.hasText(constant)) {
            return;
        }

        if (getByConstant(constant) != null) {
            throw new BusinessException("代码已经存在相同的");
        }
    }

    private Dict getByConstant(String constant) {
        DictCondition condition = new DictCondition();
        condition.setConstant(constant);
        List<Dict> list = listByCondition(condition);
        if (!CollectionUtils.isEmpty(list)) {
            return list.get(0);
        }
        return null;
    }

    private List<CacheDict> listByCondition() {
        List<CacheDict> list = cache().get("dictAll", List.class);
        if (CollectionUtils.isEmpty(list)) {
            List<Dict> dictList = super.listByCondition(new DictCondition());
            if (!CollectionUtils.isEmpty(dictList)) {
                list = new ArrayList<>(dictList.size());
                for (Dict dict : dictList) {
                    list.add(new CacheDict(dict));
                }
                cache().put("dictAll", list);
            }
        }
        return list;
    }

    @Override
    public List<TreeDTO> getTree() {
        return getTree(new DictCondition());
    }

    @Override
    public List<TreeDTO> getTree(DictCondition condition) {
        List<Dict> dictList = this.listByCondition(condition);
        if (CollectionUtils.isEmpty(dictList)) {
            return null;
        }

        List<TreeDTO> treeList = new ArrayList<>();
        TreeDTO treeDTO = null;
        for (Dict dict : dictList) {
            if (!WebConsts.ROOT.equals(dict.getParentId())) {
                continue;
            }

            treeDTO = new TreeDTO();
            treeDTO.setId(dict.getDictId());
            treeDTO.setParentId(dict.getParentId());
            // TODO 此处需要国际化
            treeDTO.setTitle(dict.getDictName());
            treeList.add(treeDTO);
        }

//        int count = 2;
        for (TreeDTO dictTree : treeList) {
            setChildren(dictTree, dictList);
//            if (count <= condition.getLevel()) {
//                setChildren(dictTree, dictList);
//                count++;
//            }
        }
        return treeList;
    }

    @Override
    public Dict getByCode(String code) {
        return baseMapper.selectByCode(code);
    }

    @Override
    public String getName(String code) {
        if (!StringUtils.hasText(code)) {
            return null;
        }

        String name = cache().get(code, String.class);
        if (StringUtils.hasText(name)) {
            return name;
        }

        Dict dict = baseMapper.selectByCode(code);
        if (dict == null) {
            return null;
        }

        name = dict.getDictName();
        if (StringUtils.hasText(name)) {
            cache().put(code, name);
        }
        return name;
    }

    @Override
    public String getCode(String constant) {
        Dict entity = getByConstant(constant);
        return entity != null ? entity.getDictCode() : null;
    }

    @Override
    public TreeSelected getTreeSelected(String constant, List<String> selectedIdList) {
        DictCondition condition = new DictCondition();
        condition.setConstant(constant);
        List<Dict> list = listByCondition(condition);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }

        List<TreeNode> nodeList = new ArrayList<>();
        TreeNode node = null;
        for (Dict dict : list) {
            node = new TreeNode();
            node.setId(dict.getDictId());
            node.setName(dict.getDictName());
            node.setParentId(dict.getParentId());
            nodeList.add(node);
        }

        TreeSelected treeSelected = new TreeSelected();
        treeSelected.setSelectedIdList(selectedIdList);
        treeSelected.setNodeList(TreeUtils.bulid(nodeList, WebConsts.ROOT));
        return treeSelected;
    }

    public String getId(String constant) {
        Dict entity = getByConstant(constant);
        return entity != null ? entity.getDictId() : null;
    }

    private void setChildren(TreeDTO dictTree, List<Dict> dictList) {
        TreeDTO treeDTO = null;

        for (Dict dict : dictList) {
            if (!dictTree.getId().equals(dict.getParentId())) {
                continue;
            }

            List<TreeDTO> children = dictTree.getChildren();
            if (children == null) {
                children = new ArrayList<>();
            }
            treeDTO = new TreeDTO();
            treeDTO.setId(dict.getDictId());
            treeDTO.setParentId(dict.getParentId());
            // TODO 此处需要国际化
            treeDTO.setTitle(dict.getDictName());
            children.add(treeDTO);
            dictTree.setChildren(children);

            setChildren(treeDTO, dictList);
        }
    }

    class CacheDict implements Serializable {
        private static final long serialVersionUID = 4179051083697228770L;

        private String code;
        private String name;

        public CacheDict(Dict dict) {
            this.code = dict.getDictCode();
            this.name = dict.getDictName();
        }

        public String getCode() {
            return code;
        }

        public void setCode(String code) {
            this.code = code;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
